#include "WorldModel.h"
#include "EntityEnemy.h"
#include "EntitySkyBox.h"
#include "EntityTerrain.h"
#include "EntityPlayer.h"
#include "EntityCamera.h"
#include "UserInterface.h"
#include "EntityPlayer.h"
#include "EntityDecor.h"
#include "Visualisation.h"
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <TXF.h>
using namespace TXF;

#include <assert.h>

CWorldModel* CWorldModel::instance=0;

CWorldModel::CWorldModel(void):m_cameraMode(eModeFPS), m_gameOver(false), m_indexPlayer(-1), m_indexTerrain(-1), m_indexCamera(-1)
{}

CWorldModel::~CWorldModel(void)
{
	for(vector<CWorldEntity*>::iterator i=m_vectorEntity.begin(); i!=m_vectorEntity.end(); i++)
	{
		delete (*i);
	}
}

CWorldModel* CWorldModel::getInstance(void)
{
	if(instance==0)
		instance=new CWorldModel();

	return instance;
}

void CWorldModel::close()
{
	delete instance;
	instance=0;
}

void CWorldModel::createEnemy(int gfxIndex, D3DXVECTOR3 initialPosition, float initialPitch, float initialYaw, float initialRoll, float speed, 
							  bool collision, int indexSound, int soundDuration, float damage, float offsetY)
{
	assert(gfxIndex>=0);

	CEntityEnemy* newEnemy = new CEntityEnemy(initialPosition, initialPitch, initialYaw, initialRoll, gfxIndex, speed, collision, indexSound,
		soundDuration, damage, offsetY);
	m_vectorEntity.push_back(newEnemy);
}

void CWorldModel::createSkyBox(int gfxIndex)
{
	assert(gfxIndex>=0);

	CEntitySkyBox* newSkyBox= new CEntitySkyBox(gfxIndex);
	m_vectorEntity.push_back(newSkyBox);
}

void CWorldModel::createTerrain(int gfxIndex)
{
	assert(gfxIndex>=0);

	CEntityTerrain* newTerrain=new CEntityTerrain(gfxIndex);
	m_indexTerrain=(int)m_vectorEntity.size();
	m_vectorEntity.push_back(newTerrain);
}

void CWorldModel::createPlayer(int gfxIndex, D3DXVECTOR3 initialPosition, float initialPitch, float initialYaw, float initialRoll, float speed, float health, bool collision)
{
	assert(gfxIndex>=0);

	CEntityPlayer* newPlayer=new CEntityPlayer(gfxIndex, initialPosition, initialPitch, initialYaw, initialRoll, speed, health, collision);
	m_indexPlayer=(int)m_vectorEntity.size();
	m_vectorEntity.push_back(newPlayer);
}

void CWorldModel::createCamera(float distanceZ, float distanceY)
{
	CEntityCamera* newCamera = new CEntityCamera(distanceZ, distanceY);
	m_indexCamera=(int)m_vectorEntity.size();
	m_vectorEntity.push_back(newCamera);
}

void CWorldModel::createDecor(int gfxIndex, D3DXVECTOR3 initialPosition, float initialPitch, float initialYaw, float initialRoll, bool collision, float offsetY)
{
	assert(gfxIndex>=0);

	CEntityDecor* newDecor = new CEntityDecor(gfxIndex, initialPosition, initialPitch, initialYaw, initialRoll, collision, offsetY);
	m_vectorEntity.push_back(newDecor);
}

void CWorldModel::doUpdate(void)
{
	/*if(modeWireframe())
	{
		VIS->setWireframeMode(!VIS->getModeWireframe());
	}*/
	
	if(this->getPlayerHealth()<=0)
	{
		//display game over
		m_gameOver=true;
	}
	else
	{
		for(vector<CWorldEntity*>::iterator i=m_vectorEntity.begin(); i!=m_vectorEntity.end(); i++)
		{
			(*i)->doUpdate();
		}
	}
}

void CWorldModel::doRender(void)
{
	for(vector<CWorldEntity*>::iterator i=m_vectorEntity.begin(); i!=m_vectorEntity.end(); i++)
	{
		(*i)->doRender();
	}
}

CWorldModel::eCameraMode CWorldModel::getCameraMode(void)
{
	return m_cameraMode;
}

void CWorldModel::setCameraMode(eCameraMode cameraMode)
{
	m_cameraMode=cameraMode;
}

void CWorldModel::movePlayerForward(float amount)
{
	CEntityPlayer* player=(CEntityPlayer*)m_vectorEntity[m_indexPlayer];
	player->moveForward(amount);
}

void CWorldModel::movePlayerBackward(float amount)
{
	CEntityPlayer* player=(CEntityPlayer*)m_vectorEntity[m_indexPlayer];
	player->moveBackward(amount);
}

void CWorldModel::strafePlayerLeft(float amount)
{
	CEntityPlayer* player=(CEntityPlayer*)m_vectorEntity[m_indexPlayer];
	player->strafeLeft(amount);
}

void CWorldModel::strafePlayerRight(float amount)
{
	CEntityPlayer* player=(CEntityPlayer*)m_vectorEntity[m_indexPlayer];
	player->strafeRight(amount);
}

void CWorldModel::turnPlayerLeftRight(float amount)
{
	CEntityPlayer* player=(CEntityPlayer*)m_vectorEntity[m_indexPlayer];
	player->turnLeftRight(amount);
}

void CWorldModel::playerLookUpDown(float amount)
{
	CEntityCamera* camera=(CEntityCamera*)m_vectorEntity[m_indexCamera];
	camera->lookUpDown(amount);
}

float CWorldModel::terrainFollowing(const D3DXVECTOR3* position)
{
	//get a reference to the terain entity
	CEntityTerrain* terrain=(CEntityTerrain*)m_vectorEntity[m_indexTerrain];

	//test if the position is in the terrain TODO: in one line
	if(position->x<terrain->getMinBound().x)
		return 0;
	if(position->x>terrain->getMaxBound().x)
		return 0;
	if(position->z<terrain->getMinBound().z)
		return 0;
	if(position->z>terrain->getMaxBound().z)
		return 0;

	//get the position of the square
	int cellX=(int)((position->x-terrain->getMinBound().x)/terrain->getCellWidth());
	int cellZ=(int)((position->z-terrain->getMinBound().z)/terrain->getCellHeight());

	//get the offset
	float dx=position->x-(terrain->getMinBound().x+cellX*terrain->getCellWidth());
	float dz=position->z-(terrain->getMinBound().z+cellZ*terrain->getCellHeight());


	//get the index of the vertices
	int indexVertex1= cellZ*(terrain->getNumCellWide()+1)+cellX;
	int indexVertex2;
	int indexVertex3;

	if(dx>dz)
	{
		indexVertex3=indexVertex1+1;
		indexVertex2=indexVertex1+terrain->getNumCellWide()+2;
	}
	else
	{
		indexVertex2=indexVertex1+terrain->getNumCellWide()+1;
		indexVertex3=indexVertex2+1;
	}

	//get the vertices
	D3DXVECTOR3 vertex1=terrain->getVertex(indexVertex1);
	D3DXVECTOR3 vertex2=terrain->getVertex(indexVertex2);
	D3DXVECTOR3 vertex3=terrain->getVertex(indexVertex3);

	//calcul the normal of the triangle
	D3DXVECTOR3 v1=(vertex2-vertex1);
	D3DXVec3Normalize(&v1, &v1);
	D3DXVECTOR3 v2=(vertex3-vertex1);
	D3DXVec3Normalize(&v2, &v2);

	D3DXVECTOR3 normal;
	D3DXVec3Cross(&normal, &v1, &v2);
	D3DXVec3Normalize(&normal, &normal);
	if(normal.y<0)
		normal.y*=-1;

	//calcul the height
	float positionY=vertex1.y+(normal.x*dx+normal.z*dz)/(-normal.y);
	return positionY;
}

CWorldEntity* CWorldModel::checkCollision(CWorldEntity* const entity)
{
	assert(entity!=0);

	for(vector<CWorldEntity*>::iterator i=m_vectorEntity.begin(); i!=m_vectorEntity.end(); i++)
	{
		if(entity!=(*i))
		{
			if(entity->getDetectCollision())
			{
				if(entity->checkCollisionWith((*i)))
					return (*i);
			}
		}
	}
	return 0;
}

bool CWorldModel::modeWireframe()
{
	//if(UI->keyPressed(DIK_M))
		return true;

//	return false;
}

D3DXVECTOR3 CWorldModel::getPlayerPosition()const
{
	return m_vectorEntity[m_indexPlayer]->getPosition();
}

float CWorldModel::getPlayerYaw()const
{
	return m_vectorEntity[m_indexPlayer]->getYaw();
}

float CWorldModel::getPlayerHealth()const
{
	CEntityPlayer* player=(CEntityPlayer*)m_vectorEntity[m_indexPlayer];
	return player->getHealth();
}

bool CWorldModel::getGameOver()const
{
	return m_gameOver;
}